import ejs from 'ejs';
import set from 'lodash/set';
import { gqcPermissionCheck } from '@kuiper/auth';

import pkg from '../../../package.json';

import { CONTACT_TYPE } from '../../common/constants';
import errorCodesBuilder from '../helpers/errorCodesBuilder';
import { duration } from '../../common/formatter';
import removeManyResolveBuilder from './removeManyResolveBuilder';
const { name } = pkg;

const errorCodes = errorCodesBuilder();
const emailTemplate = `<div style="font-size: 14px;line-height: 1.5;text-indent: 2em;color: #2A2B31;">
	<p>您好！</p>
	<p>感谢您使用知道创宇云防御ScanV监测服务，您当前正在绑定邮箱。</p>
	<p>本次请求验证码为：<span style="font-size: 18px;color: #0B6FD6;"><%= code %></span>，为保障您账号的安全性，该验证码有效期为5分钟。</p>
	<p style="font-size: 12px;color: #999EA8;margin-top: 30px;">如果您误收到此电子邮件，则可能是其他用户在尝试账号设置的误操作，可放心忽略此邮件。</p>
	<br>
</div>`;

export default server => {
	const {
		app: {
			schemaComposer,
			model: { AlertReceiver },
			config: {
				[name]: { verificationCodeResendInterval, verificationCodeExpire },
			},
		},
	} = server;

	const verificationCodeCache = (server.app.verificationCodeCache = server.cache(
		{
			cache: 'redis',
			segment: 'verification-code',
			expiresIn: verificationCodeExpire,
		}
	));

	const tc = server.methods.registerGraphQLSchema({
		name: 'AlertReceiver',
		model: AlertReceiver,
		queryFields: {
			Count: 'count',
			Many: 'findMany',
			Pagination: 'pagination',
		},
		mutationFields: {},
		onPreAddFields(tc) {
			const {
				app: { graphqlResolvers },
			} = server;

			graphqlResolvers.Query.AlertReceiverCount = graphqlResolvers.Query.AlertReceiverCount.withMiddlewares(
				[
					gqcPermissionCheck({
						required: 'cloud-monitor\x01listAlertReceiver',
						selfOnlyArgsBuilder(
							_source,
							args,
							{
								request: {
									auth: {
										credentials: { uid },
									},
								},
							}
						) {
							set(args, ['filter', 'uid'], uid);
						},
					}),
				]
			);

			graphqlResolvers.Query.AlertReceiverMany = graphqlResolvers.Query.AlertReceiverMany.withMiddlewares(
				[
					gqcPermissionCheck({
						required: 'cloud-monitor\x01listAlertReceiver',
						selfOnlyArgsBuilder(
							_source,
							args,
							{
								request: {
									auth: {
										credentials: { uid },
									},
								},
							}
						) {
							set(args, ['filter', 'uid'], uid);
						},
					}),
				]
			);

			graphqlResolvers.Query.AlertReceiverPagination = graphqlResolvers.Query.AlertReceiverPagination.withMiddlewares(
				[
					gqcPermissionCheck({
						required: 'cloud-monitor\x01listAlertReceiver',
						selfOnlyArgsBuilder(
							_source,
							args,
							{
								request: {
									auth: {
										credentials: { uid },
									},
								},
							}
						) {
							set(args, ['filter', 'uid'], uid);
						},
					}),
				]
			);
		},
	});

	tc.mongooseResolvers.createOne();
	tc.mongooseResolvers.updateOne();
	tc.mongooseResolvers.removeMany();
	schemaComposer.Mutation.addFields({
		sendVerificationCode: {
			description: '发送验证代码',
			type: 'Boolean',
			args: {
				contact: 'String!',
				type: 'String!',
			},
			async resolve(root, { contact, type }, { request }) {
				const {
					server: {
						methods: { sendEmail, sendSMS },
						app: {
							config: {
								[name]: { systemMailFrom, yunpianTplIdVerification },
							},
						},
					},
					auth: {
						credentials: { uid },
					},
				} = request;

				const verificationId = `${uid}-${type}`;

				let prevVerification;
				try {
					prevVerification = await verificationCodeCache.get(verificationId);
				} catch (err) {
					const error = errorCodes['[0A][发送验证码]获取验证码缓存失败'].from(
						err
					);
					server.log.error({ error }, error.rawMessage);
					throw error;
				}

				if (
					prevVerification &&
					Date.now() - prevVerification.datetime <
						verificationCodeResendInterval
				) {
					let waitMin =
						verificationCodeResendInterval -
						(Date.now() - prevVerification.datetime);
					waitMin = duration(Math.ceil(waitMin / (60 * 1000)) * 60 * 1000);
					const error = errorCodes[
						'[0A][发送验证码]发送过于频繁'
					].messageSuffix(`请${waitMin}后重试`);
					server.log.error({ error }, error.rawMessage);
					throw error;
				}

				const code = Math.random().toString().slice(-6);

				let sendOk;
				if (type === 'phone') {
					sendOk = await sendSMS({
						templateId: yunpianTplIdVerification,
						to: contact,
						content: {
							'#code#': code,
						},
					});
				} else {
					sendOk = await sendEmail({
						from: systemMailFrom,
						to: contact,
						title: 'ScanV 邮箱验证码',
						content: ejs.render(emailTemplate, { code }),
					});
				}

				if (!sendOk) {
					const error = errorCodes['[0A][发送验证码]发送验证码失败'];
					server.log.error({ error }, error.rawMessage);
					throw error;
				}

				try {
					await verificationCodeCache.set(verificationId, {
						code,
						contact,
						datetime: Date.now(),
					});
				} catch (err) {
					const error = errorCodes['[0A][发送验证码]保存验证码信息失败'].from(
						err
					);
					server.log.error({ error }, error.rawMessage);
				}

				request.log.info(
					{
						code,
						contact,
						type,
					},
					'发送验证代码'
				);

				return true;
			},
		},
		AlertReceiverCreateOne: {
			description: '创建联系人',
			type: 'CreateOneAlertReceiverPayload',
			args: {
				record: 'CreateOneAlertReceiverInput!',
				phoneVerificationCode: 'String',
				emailVerificationCode: 'String',
			},
			async resolve(root, args, { request }) {
				const {
					auth: {
						credentials: { uid, permissionMap },
					},
					server: {
						app: {
							model: { AlertReceiver },
						},
						methods: { ensureBalance, calcBalance },
					},
				} = request;

				const record = {
					uid:
						args.record.uid &&
						permissionMap['cloud-monitor\x01alertReceiverWithoutVerify\x010']
							? args.record.uid
							: uid,
					name: args.record.name,
					email: args.record.email,
					phone: args.record.phone,
				};
				if (permissionMap['cloud-monitor\x01alertReceiverWithoutVerify\x010']) {
					record.emailVerified = Boolean(record.email);
					record.phoneVerified = Boolean(record.phone);

					if (!args.record.uid) {
						record.uid = uid;
					}
				} else {
					record.emailVerified = false;
					record.phoneVerified = false;
				}

				if (!record.email && !record.phone) {
					throw new Error('请填入至少一个联系方式');
				}

				for (const type of ['phone', 'email']) {
					if (record[`${type}Verified`] || !record[type]) continue;
					if (!args[`${type}VerificationCode`]) {
						throw new Error(`请填写${CONTACT_TYPE[type]}验证码`);
					}

					const verificationId = `${uid}-${type}`;
					let verification;
					try {
						verification = await verificationCodeCache.get(verificationId);
					} catch (err) {
						const error = errorCodes['[0A][添加告警人]读取验证码缓存失败'].from(
							err
						);
						request.log.error({ error }, error.rawMessage);
						throw error;
					}

					if (
						!verification ||
						verification.code !== args[`${type}VerificationCode`] ||
						verification.contact !== args.record[`${type}`]
					) {
						request.log.debug(
							{
								verification,
								code: args[`${type}VerificationCode`],
							},
							`${CONTACT_TYPE[type]}验证码不匹配`
						);
						throw new Error(`${CONTACT_TYPE[type]}验证码不匹配`);
					}

					record[`${type}Verified`] = true;
				}

				const balanceAction = {
					uid: record.uid,
					ops: [{ key: 'alertReceiver', value: -1 }],
				};

				await ensureBalance(balanceAction);

				const exists = await AlertReceiver.findOne({
					uid: record.uid,
					name: record.name,
				}).catch(() => null);
				if (exists) {
					throw errorCodes['[0A][添加告警人]已添加该名称联系人'];
				}

				if (record?.phone) {
					const phoneExists = await AlertReceiver.findOne({
						uid: record.uid,
						phone: record.phone,
					}).catch(() => null);

					if (phoneExists) {
						const error = errorCodes['[OA][添加告警人]该手机号码已添加'];
						throw error;
					}
				}

				if (record?.email) {
					const emailExists = await AlertReceiver.findOne({
						uid: record.uid,
						email: record.email,
					}).catch(() => null);

					if (emailExists) {
						const error = errorCodes['[OA][添加告警人]该邮箱已添加'];
						throw error;
					}
				}

				let ret;
				try {
					ret = await new AlertReceiver(record).save();
					request.log.audit(
						{
							module: 'alertReceiver',
							activeType: 'create',
							actionMessage: '更新告警人',
							detail: `在告警设置中新增告警人${record.name}，联系方式为${
								record.email ? `邮箱：${record.email}` : ''
							}${record.phone ? `手机：${record.phone}` : ''}。`,
						},
						'新建告警人'
					);
				} catch (err) {
					const error = errorCodes['[0A][添加告警人]保存Mongo记录失败'].from(
						err
					);
					request.log.error({ error, record: args.record }, error.rawMessage);
					throw error;
				}

				request.log.info({ record }, `添加告警人联系`);

				await calcBalance(balanceAction);

				return { record: ret, recordId: ret._id };
			},
		},
		AlertReceiverUpdateOne: {
			description: '更新联系人',
			type: 'UpdateOneAlertReceiverPayload',
			args: {
				filter: 'FilterUpdateOneAlertReceiverInput!',
				record: 'UpdateOneAlertReceiverInput!',
				phoneVerificationCode: 'String',
				emailVerificationCode: 'String',
			},
			async resolve(root, args, { request }) {
				const {
					auth: {
						credentials: { uid: authUid, permissionMap },
					},
					server: {
						app: {
							model: { AlertReceiver },
						},
					},
				} = request;

				const record = args.record;
				delete record.isDefault;
				delete record.emailVerified;
				delete record.phoneVerified;

				// 检查是否有修改其他人记录的权限
				// 没有的话用先前登录用户的uid
				const uid =
					args.filter.uid &&
					permissionMap['cloud-monitor\x01editAlertReceiver\x010']
						? args.filter.uid
						: authUid;

				const prevRecord = await AlertReceiver.findOne({
					uid,
					_id: args.filter._id,
				}).catch(() => null);
				if (!prevRecord) {
					throw errorCodes['[0A][编辑告警人]未找到对应联系人'];
				}

				if (permissionMap['cloud-monitor\x01alertReceiverWithoutVerify\x010']) {
					record.emailVerified = Boolean(record.email);
					record.phoneVerified = Boolean(record.phone);

					if (!args.record.uid) {
						record.uid = uid;
					}
				}

				for (const type of ['phone', 'email']) {
					if (record[`${type}Verified`]) continue;
					if (!record[type]) {
						if (record[type] === '') {
							record[`${type}Verified`] = false;
						}
						continue;
					}
					if (
						prevRecord[`${type}Verified`] &&
						record[type] === prevRecord[type]
					) {
						continue;
					}

					if (
						record[type] !== prevRecord[type] &&
						!args[`${type}VerificationCode`]
					) {
						request.log.debug({ args }, `${CONTACT_TYPE[type]}验证码为空`);
						throw new Error(`请填写${CONTACT_TYPE[type]}验证码`);
					}

					const verificationId = `${uid}-${type}`;
					let verification;
					try {
						verification = await verificationCodeCache.get(verificationId);
					} catch (err) {
						const error = errorCodes['[0A][编辑告警人]读取验证码缓存失败'].from(
							err
						);
						request.log.error({ error }, error.rawMessage);
						throw error;
					}

					if (
						!verification ||
						verification.contact !== record[type] ||
						verification.code !== args[`${type}VerificationCode`]
					) {
						request.log.debug(
							{ verification, args },
							`${CONTACT_TYPE[type]}验证码不匹配`
						);
						throw new Error(`${CONTACT_TYPE[type]}验证码不匹配`);
					}

					record[`${type}Verified`] = true;
				}

				if (record.name) {
					try {
						const dup = await AlertReceiver.findOne({
							uid,
							_id: { $ne: args.filter._id },
							name: record.name,
						});
						if (dup) {
							throw errorCodes['[0A][编辑告警人]和已有告警人重名'];
						}
					} catch (err) {
						const error = errorCodes[
							'[0A][编辑告警人]查找重名Mongo记录失败'
						].from(err);
						request.log.error({ error, record: args.record }, error.rawMessage);
						throw error;
					}
				}

				if (record?.email && record?.email !== prevRecord?.email) {
					const emailExists = await AlertReceiver.findOne({
						uid,
						email: record?.email,
					}).catch(() => null);

					if (emailExists) {
						const error = errorCodes['[OA][添加告警人]该邮箱已添加'];
						throw error;
					}
				}

				if (record?.phone && record?.phone !== prevRecord?.phone) {
					const phoneExists = await AlertReceiver.findOne({
						uid: uid,
						phone: record?.phone,
					}).catch(() => null);

					if (phoneExists) {
						const error = errorCodes['[OA][添加告警人]该手机号码已添加'];
						throw error;
					}
				}

				try {
					Object.assign(prevRecord, record);
					prevRecord.save();
					request.log.audit(
						{
							module: 'alertReceiver',
							actionType: 'create',
							actionMessage: '更新告警人',
							detail: `告警设置中更新告警人，组名：${record.name}，联系方式：${
								record.email ? `邮箱${record.email}` : ''
							}${record.phone ? `手机${record.phone}` : ''}。`,
						},
						`编辑告警人`
					);
				} catch (err) {
					const error = errorCodes['[0A][编辑告警人]保存失败'].from(err);
					request.log.error({ error }, error.rawMessage);
					throw error;
				}

				return {
					recordId: prevRecord._id,
					record: prevRecord,
				};
			},
		},
		AlertReceiverRemoveMany: {
			description: '删除告警人',
			type: 'RemoveManyAlertReceiverPayload',
			args: {
				filter: schemaComposer.createInputTC({
					name: 'AlertReceiverRemoveManyFilterCustomInput',
					fields: {
						_ids: '[MongoID]',
					},
				}),
			},
			resolve: removeManyResolveBuilder({
				model: AlertReceiver,
				permissionRequired: 'cloud-monitor\x01removeAlertReceiver',
				balanceOps: [
					{
						key: 'alertReceiver',
						value: 1,
					},
				],
				prepareFilter: ({
					args: {
						filter: { _ids },
					},
				}) => ({ _id: { $in: _ids } }),
			}),
		},
	});
};
